package hotel.board;

import hotel.board.circuit.ICase;
import hotel.board.circuit.predefined.IPredefinedCircuit;
import hotel.dice.BuildDiceFaces;
import hotel.dice.IDice;
import hotel.exception.BuildingsMissingException;
import hotel.exception.NotEnoughMoneyException;
import hotel.exception.NotEnoughPlaceException;
import hotel.exception.NotYourPropertyException;
import hotel.exception.UnbuyablePropertyException;
import hotel.exception.WaitingOperationException;
import hotel.hotel.IProperty;

public class ExtendedBoard extends Board implements IExtendedBoard {
	
	// ATTRIBUTS
	
	private boolean buildFreeEntry;
	private boolean buildFreeBuilding;
	private boolean buildBuildings;
	private boolean buildEntry;
	private boolean buyProperty;
	private IDice<Object> diceToRoll;
	private long pay;
	private boolean canEnd;
	private Integer moveValue;
	
	// CONSTRUCTEUR

	public ExtendedBoard(String[] names, IPredefinedCircuit c) {
		super(names, c);
		beginTurn();
	}
	
	// REQUETES

	@Override
	public boolean canBuildFreeEntry() {
		return buildFreeEntry;
	}

	@Override
	public boolean canBuildFreeBuilding() {
		return buildFreeBuilding;
	}

	@Override
	public boolean canBuildBuildings() {
		return buildBuildings;
	}

	@Override
	public boolean canBuyProperty() {
		return buyProperty;
	}

	@Override
	public IDice<Object> mustRollDice() {
		return diceToRoll;
	}

	@Override
	public long mustPay() {
		return pay;
	}
	
	// COMMANDES
	
	@Override
	public void walkPlayer() {
		super.walkPlayer();
		ICase c = getCircuit().getCase(currentPlayer);
		switch (c.getState()) {
		case BUILDING_PERMIT:
			buildBuildings = true;
			listeners.firePropertyChange("buildBuildings", false, true);
			break;
		case BUY:
			buyProperty = true;
			listeners.firePropertyChange("buy", false, true);
			break;
		case FREE_BUILDING:
			buildFreeBuilding = true;
			listeners.firePropertyChange("buildFreeBuilding", false, true);
			break;
		case FREE_ENTRY:
			buildFreeEntry = true;
			listeners.firePropertyChange("buildFreeEntry", false, true);
			break;
		}
		setCanEnd(getCurrentRentable() == null);
	}
	
	@Override
	public Integer rollMoveDice() {
		boolean toPay = !beginTurn;
		Integer val = super.rollMoveDice();
		if (toPay) {
			IProperty p = getCurrentRentable();
			pay = p.getRent(getHotelsArea().getBuiltNb(p)) * val;
		} else {
			moveValue = val;
		}
		listeners.firePropertyChange("toPay", 0L, pay);
		return val;
	}

	@Override
	public void pay() throws NotEnoughMoneyException {
		if (pay == 0) {
			throw new IllegalStateException();
		}
		if (pay > currentPlayer.getBalance()) {
			throw new NotEnoughMoneyException();
		}
		currentPlayer.subMoney(pay);
	}
	
	@Override
	public void buildEntry(ICase c, int side) throws NotEnoughPlaceException, BuildingsMissingException, NotYourPropertyException, NotEnoughMoneyException {
		if (!buildEntry) {
			throw new IllegalStateException();
		}
		IProperty prop = c.getProperty(side);
		if (!buildFreeEntry) {
			long cost = prop.getEntryCost();
			if (currentPlayer.getBalance() < cost) {
				throw new NotEnoughMoneyException();
			}
			currentPlayer.subMoney(cost);
		} else {
			buildFreeEntry = false;
		}
		super.buildEntry(c, side);
	}

	@Override
	public void build(IProperty p) throws NotYourPropertyException, NotEnoughPlaceException, NotEnoughMoneyException {
		if (!buildBuildings) {
			throw new IllegalStateException();
		}
		if (!buildFreeBuilding) {
			long cost = p.getBuildingCost(getHotelsArea().getBuiltNb(p));
			if (getBuildDiceFace() == BuildDiceFaces.COST_X2) {
				cost *= 2;
			}
			if (cost > currentPlayer.getBalance()) {
				throw new NotEnoughMoneyException();
			}
			currentPlayer.subMoney(cost);
		}
		super.build(p);
	}

	@Override
	public void buy(IProperty p) throws NotEnoughMoneyException, UnbuyablePropertyException {
		super.buy(p);
		buyProperty = false;
		listeners.firePropertyChange("buy", true, false);
	}
	
	@Override
	public void endTurn() throws WaitingOperationException {
		if (!canEnd) {
			throw new WaitingOperationException();
		}
		if (moveValue != 6) {
			super.endTurn();	
		}
		beginTurn();
	}
	
	@Override
	public IProperty getCurrentRentable() {
		ICase c = getCircuit().getCase(currentPlayer);
		if (c.hasEntry(ICase.RIGHT_SIDE) && properties.get(c.getProperty(ICase.RIGHT_SIDE)) != currentPlayer) {
			return c.getProperty(ICase.RIGHT_SIDE);
		}
		if (c.hasEntry(ICase.LEFT_SIDE) && properties.get(c.getProperty(ICase.LEFT_SIDE)) != currentPlayer) {
			return c.getProperty(ICase.LEFT_SIDE);
		}
		return null;
	}
	
	// OUTILS
	
	private void setCanEnd(boolean can) {
		boolean old = canEnd;
		canEnd = can;
		listeners.firePropertyChange("canEnd", old, can);
	}
	
	private void beginTurn() {
		init();
		setBeginTurn(true);
		setCanEnd(false);
	}
	
	private void init() {
		buildFreeEntry = false;
		buildFreeBuilding = false;
		buildBuildings = false;
		buyProperty = false;
		diceToRoll = null;
		pay = 0;
		if (!beginTurn) {
			setCanEnd(true);
		}
	}

}
